/*
* Copyright (C) 2010-2012 Magnus Skjegstad
* 
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
* 
* The latest version of this library can be downloaded from http://mist-pubsub.googlecode.com
* 
*/
package com.skjegstad.mist2.storage;

import com.skjegstad.mist2.datatypes.Acknowledgement;
import com.skjegstad.mist2.datatypes.BloomfilterAcknowledgment;
import com.skjegstad.mist2.datatypes.NodeIdentifier;
import com.skjegstad.mist2.datatypes.TopicIdentifier;
import com.skjegstad.mist2.datatypes.TopicMap;
import com.skjegstad.mist2.datatypes.TopicMapEntry;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 *
 * @author Magnus Skjestad
 */
public class KnownNodesStore {
    private final ConcurrentMap<NodeIdentifier, TopicMap> nodeTopics;
    private final ConcurrentMap<NodeIdentifier, Acknowledgement> acknowledgments;
    private final ConcurrentMap<NodeIdentifier, Long> lastSeen;

    public KnownNodesStore() {
        nodeTopics = new ConcurrentHashMap();
        acknowledgments = new ConcurrentHashMap();
        lastSeen = new ConcurrentHashMap();
    }

    public NodeIdentifier put(NodeIdentifier nodeIdentifier, 
            TopicMap topics,
            Acknowledgement acknowledgment) {

        synchronized (nodeTopics) {
            this.putAcknowledgment(nodeIdentifier, acknowledgment);
            setLastSeen(nodeIdentifier);
            if (this.putTopics(nodeIdentifier, topics) != null) // return nodeIdentifier if the node existed from before
                return nodeIdentifier; //            
            return null;
        }

    }

    public void setLastSeen(NodeIdentifier nodeIdentifier) {
        lastSeen.put(nodeIdentifier, System.currentTimeMillis());
    }

    public Long getLastSeen(NodeIdentifier nodeIdentifier) {
        return lastSeen.get(nodeIdentifier);
    }

    public Acknowledgement putAcknowledgment(NodeIdentifier nodeIdentifier, Acknowledgement acknowledgment) {
        return acknowledgments.put(nodeIdentifier, acknowledgment);
    }

    public Acknowledgement getAcknowledgment(NodeIdentifier nodeIdentifier) {
        return acknowledgments.get(nodeIdentifier);
    }

    public TopicMap putTopics(NodeIdentifier nodeIdentifier, TopicMap topics) {
        return nodeTopics.put(nodeIdentifier, new TopicMap(topics));
    }

    public TopicMap getTopicsWithTtl(NodeIdentifier nodeIdentifier) {
        return nodeTopics.get(nodeIdentifier);
    }

    public TopicMap getAllKnownTopicsWithTtl() {
        TopicMap res = new TopicMap();

        // go through all topics and return the shortest ttl for each
        for (TopicMap t : nodeTopics.values()) {
            for (Entry<TopicIdentifier, TopicMapEntry> e : t.entrySet()){
                TopicMapEntry i = res.get(e.getKey());
                if (i == null || i.getTtl() < e.getValue().getTtl())
                    res.put(e.getKey(), e.getValue());
            }
        }

        return res;
    }

    public Collection<NodeIdentifier> getNodes() {
        return Collections.unmodifiableSet(nodeTopics.keySet());
    }

    public Collection<NodeIdentifier> getExpiredNodes(long maxAge) {
        List<NodeIdentifier> nodeList = new ArrayList();
        
        long maxTs = System.currentTimeMillis() - maxAge;
        for (Entry<NodeIdentifier, Long> e : lastSeen.entrySet())
            if (e.getValue() < maxTs)
                nodeList.add(e.getKey());

        return nodeList;
    }

    public void removeNode(NodeIdentifier nodeIdentifier) {
        synchronized (nodeTopics) {
            nodeTopics.remove(nodeIdentifier);
            acknowledgments.remove(nodeIdentifier);
            lastSeen.remove(nodeIdentifier);
        }
    }

    public void removeNodes(List<NodeIdentifier> nodeIdentifiers) {
        for (NodeIdentifier n : nodeIdentifiers)
            removeNode(n);
    }
    
}
